<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSS文件管理器</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }
        .container {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .controls {
            margin-bottom: 20px;
            padding: 15px;
            background-color: #f0f8ff;
            border-radius: 5px;
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            align-items: center;
        }
        .input-group {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        input, select {
            padding: 8px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        button {
            background-color: #1890ff;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #40a9ff;
        }
        button:disabled {
            background-color: #d9d9d9;
            cursor: not-allowed;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }
        th {
            background-color: #f2f2f2;
            position: sticky;
            top: 0;
        }
        tr:nth-child(even) {
            background-color: #f9f9f9;
        }
        .url-cell {
            max-width: 200px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .loading {
            text-align: center;
            padding: 20px;
            display: none;
        }
        .error {
            color: red;
            margin-top: 10px;
        }
        .preview-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.8);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 1000;
        }
        .preview-content {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            max-width: 90%;
            max-height: 90%;
            overflow: auto;
            position: relative;
        }
        .close-preview {
            position: absolute;
            top: 10px;
            right: 10px;
            background: #ff4d4f;
            color: white;
            border: none;
            width: 30px;
            height: 30px;
            border-radius: 50%;
            font-size: 16px;
            cursor: pointer;
        }
        .preview-iframe {
            width: 800px;
            height: 600px;
            border: none;
        }
        .preview-image {
            max-width: 800px;
            max-height: 600px;
        }
        .file-icon {
            width: 20px;
            height: 20px;
            margin-right: 5px;
            vertical-align: middle;
        }
        .column-selector {
            display: flex;
            gap: 10px;
            margin-top: 10px;
            flex-wrap: wrap;
        }
        .column-checkbox {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .hidden-column {
            display: none;
        }
        .copy-all-btn {
            background-color: #52c41a;
            margin-left: auto;
        }
        .copy-all-btn:hover {
            background-color: #73d13d;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>阿里云OSS文件管理器</h1>
        
        <div class="controls">
            <div class="input-group">
                <label for="accessKeyId">AccessKeyId:</label>
                <input type="text" id="accessKeyId" placeholder="输入AccessKeyId" value="">
            </div>
            <div class="input-group">
                <label for="accessKeySecret">AccessKeySecret:</label>
                <input type="password" id="accessKeySecret" placeholder="输入AccessKeySecret" value="">
            </div>
            <div class="input-group">
                <label for="region">Region:</label>
                <input type="text" id="region" placeholder="如: oss-cn-hangzhou" value="">
            </div>
            <div class="input-group">
                <label for="bucket">Bucket:</label>
                <input type="text" id="bucket" placeholder="输入Bucket名称" value="">
            </div>
            <div class="input-group">
                <label for="prefix">目录前缀:</label>
                <input type="text" id="prefix" placeholder="可选: 如 images/">
            </div>
            <button id="fetchFiles">获取文件列表</button>
            <span id="fileCount">共0个文件</span>
            <button id="copyAll" class="copy-all-btn" disabled>复制全部文件信息</button>
            <div class="error" id="errorMsg"></div>
            
            <div class="column-selector">
                <strong>显示列:</strong>
                <label class="column-checkbox">
                    <input type="checkbox" class="column-toggle" data-column="name" checked> 文件名
                </label>
                <label class="column-checkbox">
                    <input type="checkbox" class="column-toggle" data-column="size" checked> 文件大小
                </label>
                <label class="column-checkbox">
                    <input type="checkbox" class="column-toggle" data-column="time" checked> 修改时间
                </label>
                <label class="column-checkbox">
                    <input type="checkbox" class="column-toggle" data-column="url" checked> 访问URL
                </label>
                <label class="column-checkbox">
                    <input type="checkbox" class="column-toggle" data-column="actions" checked> 操作
                </label>
            </div>
        </div>
        
        <div class="loading" id="loading">
            <p>正在加载文件列表，请稍候...</p>
        </div>
        
        <table id="fileTable">
            <thead>
                <tr>
                    <th data-column="name">文件名</th>
                    <th data-column="size">文件大小</th>
                    <th data-column="time">最后修改时间</th>
                    <th data-column="url">访问URL</th>
                    <th data-column="actions">操作</th>
                </tr>
            </thead>
            <tbody id="fileList">
                <!-- 文件列表将在这里动态生成 -->
            </tbody>
        </table>
    </div>

    <!-- 文件预览容器 -->
    <div class="preview-container" id="previewContainer">
        <div class="preview-content">
            <button class="close-preview" id="closePreview">×</button>
            <div id="previewArea"></div>
        </div>
    </div>

    <!-- 引入阿里云OSS SDK -->
    <script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.17.1.min.js"></script>
    
    <script>
        // 全局变量
        let client = null;
        let allFiles = [];
        
        // DOM元素
        const fetchFilesBtn = document.getElementById('fetchFiles');
        const fileListTbody = document.getElementById('fileList');
        const fileCountSpan = document.getElementById('fileCount');
        const loadingDiv = document.getElementById('loading');
        const errorMsgDiv = document.getElementById('errorMsg');
        const previewContainer = document.getElementById('previewContainer');
        const previewArea = document.getElementById('previewArea');
        const closePreviewBtn = document.getElementById('closePreview');
        const columnToggles = document.querySelectorAll('.column-toggle');
        const copyAllBtn = document.getElementById('copyAll');
        
        // 文件类型图标映射
        const fileIcons = {
            'image': '📷',
            'pdf': '📄',
            'video': '🎬',
            'audio': '🎵',
            'archive': '🗄️',
            'text': '📝',
            'code': '💻',
            'default': '📁'
        };
        
        // 列定义
        const columns = {
            'name': { name: '文件名', visible: true },
            'size': { name: '文件大小', visible: true },
            'time': { name: '修改时间', visible: true },
            'url': { name: '访问URL', visible: true },
            'actions': { name: '操作', visible: true }
        };
        
        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            fetchFilesBtn.addEventListener('click', fetchFiles);
            closePreviewBtn.addEventListener('click', closePreview);
            copyAllBtn.addEventListener('click', copyAllFileInfo);
            
            // 列显示/隐藏切换
            columnToggles.forEach(toggle => {
                toggle.addEventListener('change', function() {
                    const column = this.dataset.column;
                    columns[column].visible = this.checked;
                    toggleColumnVisibility(column, this.checked);
                });
            });
            
            // 从本地存储加载凭证和列设置
            loadSettings();
        });
        
        // 初始化OSS客户端
        function initOSSClient() {
            const accessKeyId = document.getElementById('accessKeyId').value.trim();
            const accessKeySecret = document.getElementById('accessKeySecret').value.trim();
            const region = document.getElementById('region').value.trim();
            const bucket = document.getElementById('bucket').value.trim();
            
            if (!accessKeyId || !accessKeySecret || !region || !bucket) {
                showError('请填写所有必填字段');
                return null;
            }
            
            // 保存设置到本地存储
            saveSettings();
            
            return new OSS({
                region: region,
                accessKeyId: accessKeyId,
                accessKeySecret: accessKeySecret,
                bucket: bucket
            });
        }
        
        // 获取文件列表
        async function fetchFiles() {
            try {
                showLoading(true);
                clearError();
                
                client = initOSSClient();
                if (!client) return;
                
                allFiles = [];
                let result = null;
                let marker = null;
                const prefix = document.getElementById('prefix').value.trim();
                
                // 分页获取所有文件
                do {
                    result = await client.list({
                        marker: marker,
                        'max-keys': 100, // 每次获取100个文件
                        prefix: prefix || undefined
                    });
                    
                    if (result.objects) {
                        allFiles = allFiles.concat(result.objects);
                    }
                    
                    marker = result.nextMarker;
                } while (result.isTruncated);
                
                updateFileCount(allFiles.length);
                renderFileList(allFiles);
                copyAllBtn.disabled = allFiles.length === 0;
            } catch (error) {
                console.error('获取文件列表失败:', error);
                showError('获取文件列表失败: ' + error.message);
                copyAllBtn.disabled = true;
            } finally {
                showLoading(false);
            }
        }
        
        // 渲染文件列表
        function renderFileList(files) {
            fileListTbody.innerHTML = '';
            
            if (files.length === 0) {
                const row = document.createElement('tr');
                row.innerHTML = '<td colspan="5" style="text-align:center;">没有找到文件</td>';
                fileListTbody.appendChild(row);
                return;
            }
            
            files.forEach(file => {
                const row = document.createElement('tr');
                
                // 生成URL（设置10年有效期模拟"永久"）
                const url = client.signatureUrl(file.name, {
                    expires: 315360000 // 10年
                });
                
                // 获取文件类型和图标
                const fileType = getFileType(file.name);
                const fileIcon = fileIcons[fileType] || fileIcons.default;
                
                // 构建行HTML
                let rowHtml = '';
                
                if (columns.name.visible) {
                    rowHtml += `<td data-column="name">${fileIcon} ${file.name}</td>`;
                }
                
                if (columns.size.visible) {
                    rowHtml += `<td data-column="size">${formatFileSize(file.size)}</td>`;
                }
                
                if (columns.time.visible) {
                    rowHtml += `<td data-column="time">${new Date(file.lastModified).toLocaleString()}</td>`;
                }
                
                if (columns.url.visible) {
                    rowHtml += `<td data-column="url" class="url-cell" title="${url}">
                        <a href="${url}" target="_blank">${truncateUrl(url)}</a>
                    </td>`;
                }
                
                if (columns.actions.visible) {
                    rowHtml += `<td data-column="actions">
                        <button onclick="copyToClipboard('${url}')">复制URL</button>
                        <button onclick="previewFile('${file.name}', '${fileType}')">预览</button>
                    </td>`;
                }
                
                row.innerHTML = rowHtml;
                fileListTbody.appendChild(row);
            });
            
            // 更新表头显示状态
            updateHeaderVisibility();
        }
        
        // 复制所有文件信息
        async function copyAllFileInfo() {
            if (!allFiles || allFiles.length === 0) {
                alert('没有可复制的文件信息');
                return;
            }
            
            try {
                let textToCopy = '';
                
                allFiles.forEach(file => {
                    const url = client.signatureUrl(file.name, {
                        expires: 315360000 // 10年
                    });
                    textToCopy += `${file.name}\t${url}\n`;
                });
                
                await navigator.clipboard.writeText(textToCopy);
                alert(`已复制${allFiles.length}个文件的名称和URL到剪贴板`);
            } catch (error) {
                console.error('复制失败:', error);
                alert('复制失败: ' + error.message);
            }
        }
        
        // 切换列显示/隐藏
        function toggleColumnVisibility(column, visible) {
            // 更新表格中的列
            const cells = document.querySelectorAll(`[data-column="${column}"]`);
            cells.forEach(cell => {
                cell.classList.toggle('hidden-column', !visible);
            });
            
            // 保存设置
            saveSettings();
        }
        
        // 更新表头显示状态
        function updateHeaderVisibility() {
            const headers = document.querySelectorAll('th[data-column]');
            headers.forEach(header => {
                const column = header.dataset.column;
                header.classList.toggle('hidden-column', !columns[column].visible);
            });
        }
        
        // 预览文件
        function previewFile(filename, fileType) {
            const url = client.signatureUrl(filename, {
                expires: 3600 // 1小时有效
            });
            
            previewArea.innerHTML = '';
            
            switch(fileType) {
                case 'image':
                    previewArea.innerHTML = `<img src="${url}" class="preview-image" alt="${filename}">`;
                    break;
                case 'pdf':
                    previewArea.innerHTML = `<iframe src="${url}" class="preview-iframe"></iframe>`;
                    break;
                case 'video':
                    previewArea.innerHTML = `
                        <video controls class="preview-iframe">
                            <source src="${url}" type="video/mp4">
                            您的浏览器不支持视频预览
                        </video>
                    `;
                    break;
                case 'audio':
                    previewArea.innerHTML = `
                        <audio controls style="width: 100%">
                            <source src="${url}">
                            您的浏览器不支持音频预览
                        </audio>
                    `;
                    break;
                case 'text':
                case 'code':
                    fetch(url)
                        .then(response => response.text())
                        .then(text => {
                            previewArea.innerHTML = `<pre style="max-width:800px;max-height:600px;overflow:auto">${escapeHtml(text)}</pre>`;
                        })
                        .catch(err => {
                            previewArea.innerHTML = `<p>无法加载文本内容: ${err.message}</p>`;
                        });
                    break;
                default:
                    previewArea.innerHTML = `<p>不支持预览此文件类型</p><p>请下载后查看</p>`;
            }
            
            previewContainer.style.display = 'flex';
        }
        
        // 关闭预览
        function closePreview() {
            previewContainer.style.display = 'none';
        }
        
        // 获取文件类型
        function getFileType(filename) {
            const extension = filename.split('.').pop().toLowerCase();
            
            const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
            if (imageExtensions.includes(extension)) return 'image';
            
            if (extension === 'pdf') return 'pdf';
            
            const videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi'];
            if (videoExtensions.includes(extension)) return 'video';
            
            const audioExtensions = ['mp3', 'wav', 'ogg', 'm4a'];
            if (audioExtensions.includes(extension)) return 'audio';
            
            const archiveExtensions = ['zip', 'rar', '7z', 'tar', 'gz'];
            if (archiveExtensions.includes(extension)) return 'archive';
            
            const textExtensions = ['txt', 'log', 'ini', 'conf'];
            if (textExtensions.includes(extension)) return 'text';
            
            const codeExtensions = ['js', 'html', 'css', 'json', 'xml', 'py', 'java', 'cpp', 'c', 'php'];
            if (codeExtensions.includes(extension)) return 'code';
            
            return 'default';
        }
        
        // 更新文件计数
        function updateFileCount(count) {
            fileCountSpan.textContent = `共${count}个文件`;
        }
        
        // 显示/隐藏加载状态
        function showLoading(show) {
            loadingDiv.style.display = show ? 'block' : 'none';
            fetchFilesBtn.disabled = show;
        }
        
        // 显示错误信息
        function showError(message) {
            errorMsgDiv.textContent = message;
        }
        
        // 清除错误信息
        function clearError() {
            errorMsgDiv.textContent = '';
        }
        
        // 保存设置到本地存储
        function saveSettings() {
            const settings = {
                credentials: {
                    accessKeyId: document.getElementById('accessKeyId').value.trim(),
                    region: document.getElementById('region').value.trim(),
                    bucket: document.getElementById('bucket').value.trim()
                },
                columns: {},
                prefix: document.getElementById('prefix').value.trim()
            };
            
            // 保存列显示状态
            Object.keys(columns).forEach(col => {
                settings.columns[col] = columns[col].visible;
            });
            
            localStorage.setItem('ossManagerSettings', JSON.stringify(settings));
        }
        
        // 从本地存储加载设置
        function loadSettings() {
            const saved = localStorage.getItem('ossManagerSettings');
            if (saved) {
                try {
                    const settings = JSON.parse(saved);
                    
                    // 加载凭证
                    if (settings.credentials) {
                        document.getElementById('accessKeyId').value = settings.credentials.accessKeyId || '';
                        document.getElementById('region').value = settings.credentials.region || 'oss-cn-beijing';
                        document.getElementById('bucket').value = settings.credentials.bucket || '';
                    }
                    
                    // 加载目录前缀
                    if (settings.prefix) {
                        document.getElementById('prefix').value = settings.prefix;
                    }
                    
                    // 加载列设置
                    if (settings.columns) {
                        Object.keys(settings.columns).forEach(col => {
                            if (columns[col]) {
                                columns[col].visible = settings.columns[col];
                                const checkbox = document.querySelector(`.column-toggle[data-column="${col}"]`);
                                if (checkbox) {
                                    checkbox.checked = settings.columns[col];
                                }
                            }
                        });
                    }
                } catch (e) {
                    console.error('加载设置失败:', e);
                }
            }
        }
        
        // 辅助函数：格式化文件大小
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 B';
            const k = 1024;
            const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }
        
        // 辅助函数：缩短URL显示
        function truncateUrl(url, maxLength = 30) {
            if (url.length <= maxLength) return url;
            return url;
        }
        
        // 辅助函数：转义HTML
        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
        
        // 复制到剪贴板
        function copyToClipboard(text) {
            navigator.clipboard.writeText(text)
                .then(() => alert('URL已复制到剪贴板'))
                .catch(err => console.error('复制失败:', err));
        }
        
        // 暴露函数到全局以便按钮调用
        window.copyToClipboard = copyToClipboard;
        window.previewFile = previewFile;
    </script>
</body>
</html>